package com.pla.googlecalendarviews;

import com.google.gdata.data.calendar.CalendarEntry;
import com.google.gdata.data.calendar.ColorProperty;
import com.google.gdata.data.calendar.HiddenProperty;
import com.google.gdata.data.calendar.SelectedProperty;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.wonderly.awt.Packer;

public class Main extends JFrame implements ActionListener {

  private JPanel buttonHolder = new JPanel();
  private JTable table;
  private JPanel panel;
  private JProgressBar progressBar = new JProgressBar();
  private JButton saveButton;
  private JButton newViewButton;
  private JButton renameViewButton;
  private JButton deleteViewButton;
  private JButton applyButton;
  private final String ACCOUNTS = "Accounts";
  private final String QUIT = "Quit";
  private final String FILE = "File";
  private final String HELP = "Help";
  private final String ABOUT = "About";
  private final String APPLY = "Apply view";
  private final String RENAME_VIEW = "Rename view";
  private final String DELETE_VIEW = "Delete view";
  private final String CHECKBOX = "Checkboxes";
  private final String CHECK_ALL_SELECTED_CHECKBOXES = "Check all Selected checkboxes";
  private final String UNCHECK_ALL_SELECTED_CHECKBOXES = "Uncheck all Selected checkboxes";
  private final String INVERT_ALL_SELECTED_CHECKBOXES = "Invert all Selected checkboxes";
  private final String ACCOUNTS_COMBO_BOX_SELECTION = "accountsComboBoxSelection";
  private final String VIEWS_COMBO_BOX_SELECTION = "viewsComboBoxSelection";
  private final String CHECK_ALL_HIDDEN_CHECKBOXES = "Check all Hidden checkboxes";
  private final String UNCHECK_ALL_HIDDEN_CHECKBOXES = "Uncheck all Hidden checkboxes";
  private final String INVERT_ALL_HIDDEN_CHECKBOXES = "Invert all Hidden checkboxes";
  private final JComboBox accountsComboBox = new JComboBox();
  private ArrayList<Account> accounts = new ArrayList<Account>();
  private ArrayList<View> views = new ArrayList<View>();
  private final JComboBox viewsComboBox = new JComboBox();
  private final String COMBO_BOX_PROTOTYPE = "ThisIsAGoodSizeEmail@someDomain.com";
  private final String NEW_VIEW = "New view";
  private Main main = this;
  private ArrayList<Calendar> calendars = new ArrayList<Calendar>();
  private CalendarTableModel model;
  private final String SAVE = "Save";

  public static void main(String[] args) {
    System.out.println("Start: " + new Date());
    Utils.setLookAndFeel();
    try {
      Utils.firstTimeCheckForTables();
    } catch (Exception e) {
      JOptionPane.showMessageDialog(null, "Error while creating database tables. " + e.getLocalizedMessage(), "Error", JOptionPane.ERROR_MESSAGE);
      System.exit(0);
    }
    SwingUtilities.invokeLater(new Runnable() {

      public void run() {
        Main main = new Main();
        main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Utils.makeDisposeOnEscapeKey(main);
        main.setIconImage(Utils.getLogo());
        main.pack();
        main.setLocationRelativeTo(null);
        main.setVisible(true);
      }
    });
  }

  class CheckboxRenderer extends JCheckBox implements TableCellRenderer {

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
      if (value == null) {
        return this;
      }
      setOpaque(true);
      Calendar calendar = (Calendar) calendars.get(row);
      switch (column) {
        case 1:
          setSelected(calendar.isHidden());
          break;
        case 2:
          setSelected(calendar.isSelected());
          break;
      }
      return this;
    }
  }

  public Main() {
    super("Google Calendar Views");
    setJMenuBar(createMenuBar());
    accountsComboBox.setPrototypeDisplayValue(COMBO_BOX_PROTOTYPE);
    accountsComboBox.addItem("Loading...");
    accountsComboBox.addActionListener(this);
    accountsComboBox.setActionCommand(ACCOUNTS_COMBO_BOX_SELECTION);
    accountsComboBox.setEnabled(false);
    viewsComboBox.setPrototypeDisplayValue(COMBO_BOX_PROTOTYPE);
    viewsComboBox.addActionListener(this);
    viewsComboBox.setActionCommand(VIEWS_COMBO_BOX_SELECTION);
    viewsComboBox.setEnabled(false);
    panel = new JPanel();
    setContentPane(panel);
    Packer packer = new Packer(panel);
    int y = 0;
    packer.pack(new JLabel("Email address")).gridx(0).gridy(y).west();
    packer.pack(accountsComboBox).gridx(1).gridy(y++).west();
    packer.pack(new JLabel("Calendar views")).gridx(0).gridy(y).west();
    packer.pack(viewsComboBox).gridx(1).gridy(y++).west();
    newViewButton = new JButton(NEW_VIEW);
    newViewButton.setActionCommand(NEW_VIEW);
    newViewButton.addActionListener(this);
    newViewButton.setMnemonic(KeyEvent.VK_N);
    packer.pack(progressBar).gridx(0).gridy(y++).fillx().gridw(2);
    progressBar.setVisible(false);
    model = new CalendarTableModel();
    table = new JTable(model);
//    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.getColumnModel().getColumn(0).setPreferredWidth(290);
    table.getColumnModel().getColumn(1).setPreferredWidth(80);
    table.getColumnModel().getColumn(2).setPreferredWidth(80);
    table.getColumnModel().getColumn(3).setPreferredWidth(80);
    table.getColumnModel().getColumn(3).setCellEditor(new ColorPicker());
    table.getColumnModel().getColumn(3).setCellRenderer(new ColorRenderer(false));
    JScrollPane scrollPane = new JScrollPane(table);
    packer.pack(scrollPane).gridx(0).gridy(y++).gridw(2).fillboth();
    saveButton = new JButton(SAVE);
    saveButton.setEnabled(false);
    saveButton.setActionCommand(SAVE);
    saveButton.addActionListener(this);
    saveButton.setMnemonic(KeyEvent.VK_S);
    applyButton = new JButton(APPLY);
    applyButton.setActionCommand(APPLY);
    applyButton.addActionListener(this);
    applyButton.setMnemonic(KeyEvent.VK_A);
    deleteViewButton = new JButton(DELETE_VIEW);
    deleteViewButton.setActionCommand(DELETE_VIEW);
    deleteViewButton.addActionListener(this);
    deleteViewButton.setMnemonic(KeyEvent.VK_D);
    deleteViewButton.setEnabled(false);
    renameViewButton = new JButton(RENAME_VIEW);
    renameViewButton.setEnabled(false);
    renameViewButton.setActionCommand(RENAME_VIEW);
    renameViewButton.addActionListener(this);
    renameViewButton.setMnemonic(KeyEvent.VK_R);
    buttonHolder = new JPanel();
    packer.pack(buttonHolder).gridx(0).gridy(y++).gridw(2);
    buttonHolder.add(saveButton);
    buttonHolder.add(newViewButton);
    buttonHolder.add(applyButton);
    buttonHolder.add(renameViewButton);
    buttonHolder.add(deleteViewButton);
    loadAccounts();
  }

  private class CalendarTableModel extends AbstractTableModel {

    private String[] columns = {"Name", "Hidden", "Selected", "Color"};

    public Class getColumnClass(int c) {
      return getValueAt(0, c).getClass();
    }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
      if (aValue == null) {
        return;
      }
      Calendar calendar = (Calendar) calendars.get(rowIndex);
      Boolean b = false;
      switch (columnIndex) {
        case 1:
          b = (Boolean) aValue;
          calendar.setHidden(b.booleanValue());
          fireTableCellUpdated(rowIndex, columnIndex);
          break;
        case 2:
          b = (Boolean) aValue;
          calendar.setSelected(b.booleanValue());
          if (b) {
            calendar.setHidden(false);
            fireTableCellUpdated(rowIndex, columnIndex - 1);
          }
          fireTableCellUpdated(rowIndex, columnIndex);
          break;
        case 3:
          Color color = (Color) aValue;
          String hex = Utils.getRgbHexString(color);
          calendar.setRgb(hex);
          fireTableCellUpdated(rowIndex, columnIndex);
          break;
      }
    }

    public boolean isCellEditable(int rowIndex, int columnIndex) {
      if (columnIndex > 0) {
        return true;
      } else {
        return false;
      }
    }

    public String getColumnName(int column) {
      return columns[column];
    }

    public int getRowCount() {
      return calendars.size();
    }

    public int getColumnCount() {
      return columns.length;
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
      Calendar calendar = (Calendar) calendars.get(rowIndex);
      switch (columnIndex) {
        case 0:
          return calendar.getTitle();
        case 1:
          return new Boolean(calendar.isHidden());
        case 2:
          return new Boolean(calendar.isSelected());
        case 3:
          try {
            return Color.decode(calendar.getRgb());
          } catch (NumberFormatException e) {
            e.printStackTrace();
            return Color.WHITE;
          }
      }
      return null;
    }
  }

  private void loadAccounts() {
    SwingWorker worker = new SwingWorker() {

      protected Object doInBackground() throws Exception {
        AccountDAO dao = new AccountDAO();
        accounts = dao.get();
        return null;
      }

      protected void done() {
        accountsComboBox.removeAllItems();
        accountsComboBox.addItem("Select one...");
        for (Account account : accounts) {
          accountsComboBox.addItem(account);
        }
        accountsComboBox.setEnabled(true);
        if (accounts.size() == 0) {
          actionPerformed(new ActionEvent(this, 0, ACCOUNTS));
        }
      }
    };
    worker.execute();
  }

  private JMenuBar createMenuBar() {
    JMenuBar menuBar = new JMenuBar();
    //FILE
    JMenu menu = new JMenu(FILE);
    menu.add(createMenuItem(ACCOUNTS, KeyEvent.VK_G));
    menu.add(createMenuItem(QUIT, KeyEvent.VK_Q));
    menuBar.add(menu);
    //CHECKBOXES
    menu = new JMenu(CHECKBOX);
    menu.add(createMenuItem(CHECK_ALL_SELECTED_CHECKBOXES, KeyEvent.VK_1));
    menu.add(createMenuItem(UNCHECK_ALL_SELECTED_CHECKBOXES, KeyEvent.VK_2));
    menu.add(createMenuItem(INVERT_ALL_SELECTED_CHECKBOXES, KeyEvent.VK_3));
    menu.add(createMenuItem(CHECK_ALL_HIDDEN_CHECKBOXES, KeyEvent.VK_4));
    menu.add(createMenuItem(UNCHECK_ALL_HIDDEN_CHECKBOXES, KeyEvent.VK_5));
    menu.add(createMenuItem(INVERT_ALL_HIDDEN_CHECKBOXES, KeyEvent.VK_6));
    menuBar.add(menu);
    //HELP
    menu = new JMenu(HELP);
    menu.add(createMenuItem(ABOUT, 0));
    menuBar.add(menu);
    return menuBar;
  }

  private JMenuItem createMenuItem(String title, int keyEvent) {
    JMenuItem item = new JMenuItem(title);
    item.addActionListener(this);
    item.setActionCommand(title);
    if (keyEvent > 0) {
      item.setMnemonic(keyEvent);
      item.setAccelerator(KeyStroke.getKeyStroke(keyEvent, ActionEvent.CTRL_MASK));
    }
    return item;
  }

  private void loadViewsComboBox(final Account account) {
    viewsComboBox.removeAllItems();
    viewsComboBox.setEnabled(false);
    SwingWorker worker = new SwingWorker() {

      protected Object doInBackground() throws Exception {
        ViewDAO viewDAO = new ViewDAO();
        ArrayList<View> views = viewDAO.get(account.getAccountId());
        for (View view : views) {
          viewsComboBox.addItem(view);
        }
        return null;
      }

      protected void done() {
        viewsComboBox.setEnabled(true);
        viewsComboBox.addActionListener(main);
        if (viewsComboBox.getItemCount() == 0) {
          getNewViewName(account);
        }
      }
    };
    worker.execute();
  }

  private void getNewViewName(final Account account) {
    final String viewName = JOptionPane.showInputDialog(main, "Please key a view name for " + account.getEmailAddress());
    if (Utils.isBlank(viewName)) {
      accountsComboBox.setSelectedIndex(0);
      return;
    }
    able(false);
    progressBar.setVisible(true);
    progressBar.setIndeterminate(true);
    SwingWorker worker = new SwingWorker() {

      private View view;

      protected Object doInBackground() throws Exception {
        CalendarDAO calendarDAO = new CalendarDAO();
        view = new View();
        view.setName(viewName);
        view.setAccountId(account.getAccountId());
        view.setCalendars(calendarDAO.getCalendars(account.getEmailAddress(), account.getPassword()));
        ViewDAO viewDAO = new ViewDAO();
        viewDAO.updateView(view);
        return null;
      }

      protected void done() {
        viewsComboBox.addActionListener(main);
        viewsComboBox.addItem(view);
        viewsComboBox.setSelectedIndex(viewsComboBox.getItemCount() - 1);
        able(true);
        progressBar.setVisible(false);
      }
    };
    worker.execute();
  }

  public void actionPerformed(ActionEvent e) {
    String actionCommand = e.getActionCommand();
    if (QUIT.equals(actionCommand)) {
      System.exit(0);
    }
    if (ACCOUNTS.equals(actionCommand)) {
      new AccountsDialog(main);
      loadAccounts();
    }
    if (ABOUT.equals(actionCommand)) {
      new AboutDialog(main);
    }
    if (ACCOUNTS_COMBO_BOX_SELECTION.equals(actionCommand)) {
      int index = accountsComboBox.getSelectedIndex();
      if (index < 1) {
        viewsComboBox.setEnabled(false);
        calendars.clear();
        model.fireTableDataChanged();
        saveButton.setEnabled(false);
        applyButton.setEnabled(false);
        newViewButton.setEnabled(false);
        renameViewButton.setEnabled(false);
        deleteViewButton.setEnabled(false);
        return;
      }
      newViewButton.setEnabled(true);
      Account account = (Account) accounts.get(index - 1);
      loadViewsComboBox(account);
    }
    if (VIEWS_COMBO_BOX_SELECTION.equals(actionCommand)) {
      calendars.clear();
      View view = (View) viewsComboBox.getSelectedItem();
      if (view == null) {
        return;
      }
      calendars.addAll(view.getCalendars());
      model.fireTableDataChanged();
      saveButton.setEnabled(true);
      applyButton.setEnabled(true);
      renameViewButton.setEnabled(true);
      deleteViewButton.setEnabled(true);
    }
    if (SAVE.equals(actionCommand)) {
      able(false);
      progressBar.setVisible(true);
      progressBar.setIndeterminate(true);
      SwingWorker worker = new SwingWorker() {

        protected Object doInBackground() throws Exception {
          ViewDAO viewDAO = new ViewDAO();
          View view = (View) viewsComboBox.getSelectedItem();
          viewDAO.updateView(view);
          return null;
        }

        protected void done() {
          progressBar.setVisible(false);
          able(true);
        }
      };
      worker.execute();
    }
    if (NEW_VIEW.equals(actionCommand)) {
      int index = accountsComboBox.getSelectedIndex();
      Account account = (Account) accounts.get(index - 1);
      getNewViewName(account);
    }
    if (APPLY.equals(actionCommand)) {
      View view = (View) viewsComboBox.getSelectedItem();
      try {
        applyView(view);
      } catch (Exception ex) {
        ex.printStackTrace();
        JOptionPane.showMessageDialog(main, ex.getLocalizedMessage(), "Error", JOptionPane.ERROR_MESSAGE);
      }
    }
    if (CHECK_ALL_SELECTED_CHECKBOXES.equals(actionCommand)) {
      for (Calendar calendar : calendars) {
        calendar.setSelected(true);
      }
      model.fireTableDataChanged();
    }
    if (UNCHECK_ALL_SELECTED_CHECKBOXES.equals(actionCommand)) {
      for (Calendar calendar : calendars) {
        calendar.setSelected(false);
      }
      model.fireTableDataChanged();
    }
    if (INVERT_ALL_SELECTED_CHECKBOXES.equals(actionCommand)) {
      for (Calendar calendar : calendars) {
        calendar.setSelected(!calendar.isSelected());
      }
      model.fireTableDataChanged();
    }
    if (CHECK_ALL_HIDDEN_CHECKBOXES.equals(actionCommand)) {
      for (Calendar calendar : calendars) {
        calendar.setHidden(true);
      }
      model.fireTableDataChanged();
    }
    if (UNCHECK_ALL_HIDDEN_CHECKBOXES.equals(actionCommand)) {
      for (Calendar calendar : calendars) {
        calendar.setHidden(false);
      }
      model.fireTableDataChanged();
    }
    if (INVERT_ALL_HIDDEN_CHECKBOXES.equals(actionCommand)) {
      for (Calendar calendar : calendars) {
        calendar.setHidden(!calendar.isHidden());
      }
      model.fireTableDataChanged();
    }
    if (DELETE_VIEW.equals(actionCommand)) {
      deleteView();

    }
    if (RENAME_VIEW.equals(actionCommand)) {
      renameView();
    }
  }

  private void deleteView() {
    final View view = (View) viewsComboBox.getSelectedItem();
    if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(main, "Are you sure you want to delete view " + view.getName())) {
      return;
    }
    able(false);
    SwingWorker worker = new SwingWorker() {

      @Override
      protected Object doInBackground() throws Exception {
        ViewDAO viewDAO = new ViewDAO();
        viewDAO.delete(view);
        return null;
      }

      protected void done() {
        able(true);
        accountsComboBox.setSelectedIndex(0);
      }
    };
    worker.execute();
  }

  private void renameView() {
    final View view = (View) viewsComboBox.getSelectedItem();
    String newName = JOptionPane.showInputDialog("View name", view.getName());
    if (Utils.isBlank(newName) || newName.equals(view.getName())) {
      return;
    }
    able(false);
    view.setName(newName);
    SwingWorker worker = new SwingWorker() {

      @Override
      protected Object doInBackground() throws Exception {
        ViewDAO viewDAO = new ViewDAO();
        viewDAO.rename(view);
        return null;
      }

      protected void done() {
        able(true);
        accountsComboBox.setSelectedIndex(0);
      }
    };
    worker.execute();
  }

  private void applyView(final View view) {
    progressBar.setVisible(true);
    progressBar.setIndeterminate(true);
    progressBar.setStringPainted(false);
    able(false);

    SwingWorker worker = new SwingWorker() {

      private StringBuilder message = new StringBuilder();

      protected Object doInBackground() throws Exception {
        try {
          applyViewWork(view, message);
        } catch (Exception e) {
          e.printStackTrace();
          JOptionPane.showMessageDialog(main, "Error while applying view. Error text: " + e.getLocalizedMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        }
        return null;
      }

      protected void done() {
        progressBar.setVisible(false);
        if (message.length() > 0) {
          JOptionPane.showMessageDialog(main, "These calendars were added to your view.\nBe sure to save your view.\n" + message.toString(), "New calendars found", JOptionPane.INFORMATION_MESSAGE);
        }
        able(true);
      }
    };
    worker.execute();
  }

  private void applyViewWork(View view, StringBuilder message) throws Exception {
    CalendarDAO calendarDAO = new CalendarDAO();
    AccountDAO accountDAO = new AccountDAO();
    Account account = accountDAO.get(view.getAccountId());
    ArrayList<Calendar> calendarsFromGoogle = calendarDAO.getCalendars(account.getEmailAddress(), account.getPassword());
    ArrayList<Calendar> calendarsFromView = view.getCalendars();
    HashMap<String, Calendar> hashMap = new HashMap<String, Calendar>();
    for (Calendar calendarFromView : calendarsFromView) {
      hashMap.put(calendarFromView.getCalendarId(), calendarFromView);
      System.out.println("Putting calendar in hashmap " + calendarFromView.getCalendarId());
    }
    progressBar.setMaximum(calendarsFromGoogle.size());
    progressBar.setIndeterminate(false);
    progressBar.setStringPainted(true);
    int i = 0;
    int quantity = calendarsFromGoogle.size();
    for (Calendar calendarFromGoogle : calendarsFromGoogle) {
      Calendar calendarFromView = hashMap.get(calendarFromGoogle.getCalendarId());
      if (calendarFromView == null) {
        System.out.println("Calendar from Google not found in view: " + calendarFromGoogle.getCalendarId());
        calendarFromView = new Calendar(calendarFromGoogle.getCalendarEntry());
        view.getCalendars().add(calendarFromView);
        calendars.add(calendarFromView);
        message.append(calendarFromGoogle.getTitle()).append("\n");
      }
      System.out.println("Getting calendar from hashmap " + calendarFromGoogle.getCalendarId() + " color: \"" + calendarFromView.getRgb() + "\"");
      CalendarEntry calendarEntry = calendarFromGoogle.getCalendarEntry();
      SelectedProperty selectedProperty = calendarEntry.getSelected();
      selectedProperty.setValue(Boolean.toString(calendarFromView.isSelected()));
      HiddenProperty hiddenProperty = calendarEntry.getHidden();
      hiddenProperty.setValue(Boolean.toString(calendarFromView.isHidden()));
      ColorProperty colorProperty = calendarEntry.getColor();
      System.out.println("ColorProperty.getValue() " + colorProperty.getValue());
      String rgb = calendarFromView.getRgb();
      if (!Utils.isBlank(rgb)) {
        colorProperty.setValue(rgb.toUpperCase());
      }
      try {
        calendarEntry.update();
      } catch (Exception e) {
        e.printStackTrace();
      }
      i++;
      progressBar.setValue(i);
      progressBar.setString("Applying view on " + i + " out " + quantity + " calendars");
    }
  }

  private void able(boolean b) {
    Component[] components = panel.getComponents();
    table.setEnabled(b);
    for (Component component : components) {
      if (!component.getClass().getName().endsWith("JProgressBar")) {
        component.setEnabled(b);
      }
    }
    components = buttonHolder.getComponents();
    for (Component component : components) {
      component.setEnabled(b);
    }
  }
}
